using System;
using System.Collections.Generic;
using Ninject;

namespace CardsEngine.Poker
{
    [Obsolete]
	public class PokerHandEvaluator
	{
        /// <summary>
        /// The default math functions provider; custom math function
        /// providers may be used by calling one of the appropriate constructors
        /// </summary>
        public static readonly IMathFunctionProvider DefaultMathProvider;
        private IDictionary<Hand, UInt64> handValueLookupTable;

        public IMathFunctionProvider math;

        static PokerHandEvaluator()
        {
            DefaultMathProvider = new PokerTestKit.BasicMathProvider();
        }
        
        /// <summary>
        /// Use the default math function provider for math functions and
        /// initializes a System.Collections.Generic.Dictionary for the
        /// IDictionary dependency
        /// </summary>
        public PokerHandEvaluator()
        {
            math = DefaultMathProvider;
            handValueLookupTable = new Dictionary<Hand, UInt64>();
            InitializeInstance();
        }

        /// <summary>
        /// Use a custom math function provider and initializes a
        /// System.Collections.Generic.Dictionary for the IDictionary dependency
        /// </summary>
        /// <param name="_math">The math functions provider</param>
        public PokerHandEvaluator(IMathFunctionProvider _math)
        {
            math = DefaultMathProvider;
            handValueLookupTable = new Dictionary<Hand, UInt64>();
            InitializeInstance();
        }

        /// <summary>
        /// Use the default math function provider and allow a custom
        /// fulfillment of the IDictionary dependency. 
        /// </summary>
        /// <param name="dict_inject">An implementation of IDictionary</param>
        /// <remarks>Note that the dict_inject parameter's Clear() method will be called by this constructor</remarks>
        public PokerHandEvaluator(IDictionary<Hand, UInt64> dict_inject)
        {
            math = new PokerTestKit.BasicMathProvider();
            handValueLookupTable = dict_inject;
            InitializeInstance();
        }

        /// <summary>
        /// Use a custom math function provider and custom implementation of the IDictionary
        /// dependency.
        /// </summary>
        /// <param name="dict_inject">An implementation of IDictionary</param>
        /// <param name="_math">The math functions provider</param>
        /// <remarks>Note that the dict_inject parameter's Clear() method will be called by this constructor</remarks>
        public PokerHandEvaluator(IDictionary<Hand, UInt64> dict_inject, IMathFunctionProvider _math)
        {
            math = _math;

        }

        private void InitializeInstance()
        {
            handValueLookupTable.Clear();
        }

        protected ISet<Hand> GetAllSubsets(Hand cards, int subsetSize)
        {

            Hand current;
            ISet<Hand> result = new HashSet<Hand>();
            int totalNumCards = cards.Count;

            if (totalNumCards < subsetSize)
                throw new ArgumentException("The size (Count) of the cards list must be at least 5");

            // Initial value to subsetSize many 1's in the LO sector
            // i.e. if subsetSize=5 then choose's initial value should be 0000 ... 0001 1111
            // Such a value indicates that the first loop iteration will choose the cards at indices 0-4
            uint choose = 0;
            for (int x = 0; x < subsetSize; ++x)
                choose |= (uint)(1 << x);

            // The maximum value allowed for "choose"
            // ex if totalNumCards is 7
            // then max_choose_value = 0000 ... 0111 1100 = 0x7C
            uint max_choose_value = choose << (totalNumCards-subsetSize);

            while (choose < max_choose_value)
            {
                current = new Hand();
                for (int i = 0; i < totalNumCards; i++)
                {
                    // If the i'th bit of 'choose' is set
                    if ((choose & (1 << i)) != 0)
                        current.Add(cards[i]);
                }

                result.Add(current);

                // This is a lazy way of doing the increment...
                // There's probably a better way to do it using bitwise operations
                // With the current method, we simply increment by 1 repeatedly until
                // the hamming weight is equal to the subset size
                do { ++choose; } while (math.ComputeHammingWeight32(choose) != subsetSize);
            }

            return result;
        }

        public virtual UInt64 ComputeFiveCardHandStrength(Hand cards)
        {
            if (cards.Count < 5)
                throw new ArgumentException("There must be at least 5 cards to compute the strength of the hand using this method");
            else if (cards.Count > 5)
            {
                // TODO - Compute hand strength of all possible 5-card sets,
                // then select the set with the highest strength.
                ISet<IList<Card>> fiveCardSets = GetAllSubsets(cards, 5);

            }

            throw new NotImplementedException();
        }
	}
}

